home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
mint
/
mint110s.zoo
/
nalloc2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-02-09
|
6KB
|
269 lines
/*
* Copyright 1992,1993 Atari Corporation.
* All rights reserved.
*/
/*
* General-purpose memory allocator, on the MWC arena model, with
* this added feature:
*
* All blocks are coalesced when they're freed. If this results in
* an arena with only one block, and that free, it's returned to the
* OS.
*
* The functions here have the same names and bindings as the MWC
* memory manager, which is the same as the UNIX names and bindings.
*
* MiNT version: used for kmalloc to manage kernel memory in small hunks,
* rather than using a page at a time.
*/
#include "mint.h"
#if 0
#define NALLOC_DEBUG(c) TRACE(("nalloc: %c",c));
#else
#define NALLOC_DEBUG(c) /* nothing */
#endif
#define NKMAGIC 0x19870425L
/*
* block header: every memory block has one.
* A block is either allocated or on the free
* list of the arena. There is no allocated list: it's not necessary.
* The next pointer is only valid for free blocks: for allocated blocks
* maybe it should hold a verification value..?
*
* Zero-length blocks are possible and free; they hold space which might
* get coalesced usefully later on.
*/
struct block {
struct block *b_next; /* NULL for last guy; next alloc or next free */
long b_size;
};
/*
* arena header: every arena has one. Each arena is always completely
* filled with blocks; the first starts right after this header.
*/
struct arena {
struct arena *a_next;
struct block *a_ffirst;
long a_size;
};
/*
* Arena linked-list pointer, and block size.
*/
static struct arena *a_first = (struct arena *)NULL;
void
nalloc_arena_add(start,len)
void *start;
long len;
{
struct arena *a;
struct block *b;
for (a = a_first; a && a->a_next; a = a->a_next)
continue;
if (a)
a->a_next = (struct arena *)start;
else
a_first = (struct arena *)start;
a = start;
a->a_next = NULL;
a->a_ffirst = b = (struct block *)(a+1);
a->a_size = len - sizeof(*a);
b->b_next = NULL;
b->b_size = (len - sizeof(*a) - sizeof(*b));
}
void *
nalloc(size)
long size;
{
struct arena *a;
struct block *b, *mb, **q;
long temp;
NALLOC_DEBUG('A');
/* force even-sized alloc's */
size = (size+1) & ~1;
for (a = a_first; a; a = a->a_next) {
for (b = *(q = &a->a_ffirst); b; b = *(q = &b->b_next)) {
/* if big enough, use it */
if (b->b_size >= (size + sizeof(struct block))) {
/* got one */
mb = b;
/* cut the free block into an allocated part & a free part */
temp = mb->b_size - size - sizeof(struct block);
if (temp >= 0) {
/* large enough to cut */
NALLOC_DEBUG('c');
b = (struct block *)(((char *)(b+1)) + size);
b->b_size = temp;
b->b_next = mb->b_next;
*q = b;
mb->b_size = size;
mb->b_next = (struct block *)NKMAGIC;
}
else {
/* not big enough to cut: unlink this from free list */
NALLOC_DEBUG('w');
*q = mb->b_next;
}
return (void *)(mb+1);
}
}
}
/* no block available: get a new arena */
#if 1
return NULL; /* MiNT: fail. */
#else
if (!minarena) {
minarena = Malloc(-1L) / 20;
if (minarena > MAXARENA) minarena = MAXARENA;
}
if (size < minarena) {
NALLOC_DEBUG('m');
temp = minarena;
}
else {
NALLOC_DEBUG('s');
temp = size;
}
a = (struct arena *)Malloc(temp +
sizeof(struct arena) +
sizeof(struct block));
/* if Malloc failed return failure */
if (a == 0) {
NALLOC_DEBUG('x');
return 0;
}
a->a_size = temp + sizeof(struct block);
a->a_next = a_first;
a_first = a;
mb = (struct block *)(a+1);
mb->b_next = NULL;
mb->b_size = size;
if (temp > (size + sizeof(struct block))) {
NALLOC_DEBUG('c');
b = a->a_ffirst = ((char *)(mb+1)) + size;
b->b_next = NULL;
b->b_size = temp - size - sizeof(struct block);
}
else {
a->a_ffirst = NULL;
}
return (void *)(++mb);
#endif
}
void
nfree(start)
void *start;
{
struct arena *a, **qa;
struct block *b;
struct block *pb;
struct block *fb = (struct block *)start;
NALLOC_DEBUG('F');
if (fb->b_next != (struct block *)NKMAGIC) {
FATAL("nfree: block %lx not allocated by nalloc!",fb);
}
/* set fb (and b) to header start */
b = --fb;
/* the arena this block lives in */
for (a = *(qa = &a_first); a; a = *(qa = &a->a_next)) {
if ((unsigned long)b > (unsigned long)a &&
(unsigned long)b < (((unsigned long)a) + a->a_size)) goto found;
}
FATAL("nfree: block %lx not in any arena!",fb);
found:
/* Found it! */
/* a is this block's arena */
/* set pb to the previous free block in this arena, b to next */
for (pb = NULL, b = a->a_ffirst;
b && (b < fb);
pb = b, b = b->b_next) ;
fb->b_next = b;
/* Coalesce backwards: if any prev ... */
if (pb) {
/* if it's adjacent ... */
if ((((unsigned long)(pb+1)) + pb->b_size) == (unsigned long)fb) {
NALLOC_DEBUG('b');
pb->b_size += sizeof(struct block) + fb->b_size;
fb = pb;
}
else {
/* ... else not adjacent, but there is a prev free block */
/* so set its next ptr to fb */
pb->b_next = fb;
}
}
else {
/* ... else no prev free block: set arena's free list ptr to fb */
a->a_ffirst = fb;
}
/* Coalesce forwards: b holds start of free block AFTER fb, if any */
if (b && (((unsigned long)(fb+1)) + fb->b_size) == (unsigned long)b) {
NALLOC_DEBUG('f');
fb->b_size += sizeof(struct block) + b->b_size;
fb->b_next = b->b_next;
}
/* if, after coalescing, this arena is entirely free, Mfree it! */
if ((struct arena *)a->a_ffirst == a+1 &&
(a->a_ffirst->b_size + sizeof(struct block))== a->a_size &&
a != a_first) {
NALLOC_DEBUG('!');
*qa = a->a_next;
#if 1
kfree(a); /* MiNT -- give back so it can be used by users */
#else
(void)Mfree(a);
#endif
}
return;
}
void
NALLOC_DUMP()
{
struct arena *a;
struct block *b;
for (a = a_first; a; a = a->a_next) {
FORCE("Arena at %lx size %lx: free list:",a,a->a_size);
for (b = a->a_ffirst; b; b = b->b_next) {
FORCE(" %8lx size %8lx",b,b->b_size);
}
}
}